/*
 * CHttpServer.h
 *
 *  Created on: 2016-7-25
 *      Author: terry
 */

#ifndef CHTTPSERVER_H_
#define CHTTPSERVER_H_

#include "BasicType.h"
#include "HttpServer.h"
#include "TThread.h"
#include "TCriticalSection.h"
#include "TEvent.h"
#include <string>
#include <map>
#include <vector>
#include <deque>

/**
 * HTTP服务器实现
 * 內建的URL:
 *  /upload 表示上传文件,
 *  /api/json/ 表示JSON HTTP API,
 *  /api/sys/password 表示修改密码
 *  /api/file 表示一般的动态响应
 *  /api/ws 表示 websocket
 *
 */
class CHttpServer: public HttpServer, comn::Thread
{
public:
	static const int ASYNC_DURATION = 60;

public:
	CHttpServer();
	virtual ~CHttpServer();


	virtual bool setWebRoot(const char* dirPath);

	virtual bool setUploadDir(const char* dirPath);

	virtual bool setUserFile(const char* filePath);

	virtual bool addDir(const char* url, const char* dirPath);
	
	virtual bool addJsonApi(const char* prefix);

	virtual bool start(int port, HttpHandler* handler);

	virtual void stop();

	virtual bool isStarted();

	virtual void broadcast(void *data, size_t len);

	virtual bool sendResponse(mg_connection* nc, int code, const std::string& content);

	virtual bool send(mg_connection* nc, const void* data, size_t len);

	virtual void broadcastBinary(void *data, size_t len);

public:
	void handleHttpEvent(struct mg_connection *nc, int ev, struct http_message *hm);

	void handleUpload(struct mg_connection *nc, int ev, void *ev_data);

	void handleDirEntry(struct mg_connection *nc, int ev, void *ev_data);

	void handleWebSocketFrame(struct mg_connection *nc, int ev, struct websocket_message* wm);

	void handleAsyncResult(struct mg_connection *nc, int ev, void *ev_data);

	static void sendJsonResponse(mg_connection *nc, int code, const char *reason, const std::string& json);
	static void sendJsonResponse(mg_connection *nc, int code, const char *reason, const char* json);
	static void sendResponse(mg_connection *nc, int code, const char *reason,
		const std::string& origin,
		const std::string& mime,
		const char* content);
	static void sendResponse(mg_connection *nc, int code, const char *reason,
		const std::string& origin,
		const std::string& mime,
		const std::string& content);

protected:
	virtual int run();
	virtual void doStop();

protected:
	void handleJsonApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri);
	void handleSysApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri);
	void handleFileApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri);

	std::string	makeUploadPath(const char* filename);

	bool setupUser();

	std::string makeUser(const std::string& name, const std::string& realm, const std::string& passwd);

	bool resetUser(const std::string& name, const std::string& passwd);


	struct DirEntry
	{
		std::string url;
		std::string	dirPath;
		mg_serve_http_opts opts;

		DirEntry():
			opts()
		{
		}
	};

	typedef std::map< std::string, DirEntry>	DirEntryMap;

	bool findDirEntry(const char* url, DirEntry& entry);
	bool findDirEntry(struct mg_str& url, DirEntry& entry);


	typedef std::map< std::string, std::string>	PrefixMap;
	bool findPrefix(const struct mg_str& url, std::string& prefix);

	struct AsyncEntry
	{
		HttpAsyncResult result;
		time_t	expired;

		AsyncEntry():
			result(),
			expired()
		{
		}
	};

	typedef std::map< mg_connection*, AsyncEntry> AsyncResultMap;

	void clearExpiredAsyncResult();
	bool fetchAsyncResult(mg_connection* connection, HttpAsyncResult& result);
	void signalAsyncResult();
	bool putAsyncResult(HttpAsyncResult& result);

public:
	struct Frame
	{
		int seq;
		std::string data;

		Frame():
			seq()
		{
		}

		Frame(int s, const std::string& d) :
			seq(s),
			data(d)
		{
		}
	};

	typedef std::deque< Frame >		FrameDeque;

	bool fetchFrame(int seq, Frame& frame);

protected:
	HttpHandler*	m_handler;
	std::string	m_webRoot;
	std::string m_port;
	std::string m_uploadDir;
	std::string m_userFile;

	struct mg_mgr	m_mgr;
	mg_serve_http_opts	m_opts;
	struct mg_connection*	m_connection;

	DirEntryMap	m_entryMap;
	PrefixMap	m_prefixMap;

	comn::CriticalSection	m_cs;
	AsyncResultMap	m_asyncResultMap;

	int	m_frameSeq;
	FrameDeque	m_frameDeque;

};

#endif /* CHTTPSERVER_H_ */
